
#include "RtBase.h"
#include "RtUdpEndpoint.h"
#include "RtTransportUdp.h"
#include "RtInetAddr.h"

CRtString RtGetSystemErrorInfo(int inErrno);

CRtTransportUdp::CRtTransportUdp(
		IRtReactor* pReactor, 
		CRtUdpEndpoint* pEndpoint,
		const CRtInetAddr& aAddrPeer, 
		BOOL bAcceptor)
	: CRtTransportBase(pReactor)
	, m_endpoint(pEndpoint)
	, m_addrPeer(aAddrPeer)
	, m_bAcceptor(bAcceptor)
{
}

CRtTransportUdp::~CRtTransportUdp()
{
	Close_t(RT_OK);
}

RtResult CRtTransportUdp::Close_t(RtResult aReason)
{
	if (m_endpoint)
	{
		m_endpoint->RemoveTransport(m_addrPeer);
		m_endpoint = NULL;
	}
	return RT_OK;
}

RtResult CRtTransportUdp::SendData(
		CRtMessageBlock& inData,
		BOOL inSendImmediately,
		BOOL inDestroyData,
		DataPriority inPriority, 
		BOOL inNeedAck)
{
	if (m_endpoint==NULL || m_endpoint->GetHandle() == RT_INVALID_HANDLE) {
		RT_WARNING_TRACE_THIS("CRtTransportUdp::SendData, socket is invalid. endpoint="<<m_endpoint);
		return RT_ERROR_NOT_INITIALIZED;
	}

	const CRtMessageBlock* tmpData = &inData;
	static iovec iov[RT_IOV_MAX];
	DWORD fillLen = 0;
	DWORD iovNum = tmpData->FillIov(iov, RT_IOV_MAX, fillLen, tmpData);
	RT_ASSERTE_RETURN(iovNum > 0, RT_OK);
	RT_ASSERTE(tmpData==NULL);

	int nSend = 0;
	do {
		nSend = m_endpoint->GetSocket().SendVTo(iov, iovNum, m_addrPeer);
	} while (nSend == 0);

	if (nSend == -1) {
		RtResult rv = RT_ERROR_PARTIAL_DATA;
		if (errno!=EWOULDBLOCK)
		{
			rv = RT_ERROR_NETWORK_SOCKET_ERROR;
			RT_WARNING_TRACE_THIS("CRtTransportUdp::SendData, SendVTo() failed!"
				" fillLen=" << fillLen <<
				" iovNum=" << iovNum <<
				" addr=" << m_addrPeer.GetIpDisplayName() <<
				" port=" << m_addrPeer.GetPort() <<
				" errno=" << RtGetSystemErrorInfo(errno));
		}

		return rv;
	}

	if (inDestroyData)
		inData.DestroyChained();

	return RT_OK;
}

RtResult CRtTransportUdp::GetOption(DWORD aCommand, LPVOID aArg)
{
	if (m_endpoint==NULL)
		return RT_ERROR_NETWORK_SOCKET_CLOSE;

	RT_ASSERTE_RETURN(aArg, RT_ERROR_INVALID_ARG);

	switch (aCommand) {
	case RT_OPT_TRANSPORT_FIO_NREAD:
		if (m_endpoint->GetSocket().Control(FIONREAD, aArg) == -1) {
			RT_WARNING_TRACE_THIS("CRtTransportUdp::GetOption, (RT_OPT_TRANSPORT_FIO_NREAD) failed! err=" << errno);
			return RT_ERROR_NETWORK_SOCKET_ERROR;
		}
		return RT_OK;

	case RT_OPT_TRANSPORT_FD:
		*(static_cast<RT_HANDLE *>(aArg)) = m_endpoint->GetHandle();
		return RT_OK;

	case RT_OPT_TRANSPORT_LOCAL_ADDR:
#if 0
		if (m_endpoint->GetSocket().GetLocalAddr(*(static_cast<CRtInetAddr*>(aArg))) == -1) {
			RT_WARNING_TRACE_THIS("CRtTransportUdp::GetOption, (RT_OPT_TRANSPORT_LOCAL_ADDR) failed!"
				" fd=" << m_endpoint->GetHandle() << " err=" << errno);
			return RT_ERROR_NETWORK_SOCKET_ERROR;
		}
		else
			return RT_OK;
#else
		*(static_cast<CRtInetAddr*>(aArg)) = m_endpoint->GetLocalAddr();
		return RT_OK;
#endif

	case RT_OPT_TRANSPORT_PEER_ADDR:
		*(static_cast<CRtInetAddr*>(aArg)) = m_addrPeer;
		return RT_OK;

	case RT_OPT_TRANSPORT_TRAN_TYPE:
		*(static_cast<CRtConnectionManager::CType*>(aArg)) = CRtConnectionManager::CTYPE_UDP;
		return RT_OK;

	case RT_OPT_TRANSPORT_SND_BUF_LEN: {
		int nLen = sizeof(DWORD);
		if (m_endpoint->GetSocket().GetOption(SOL_SOCKET, SO_SNDBUF, aArg, &nLen) == -1) {
			RT_ERROR_TRACE_THIS("CRtTransportUdp::GetOption, GetOption(SO_SNDBUF) failed!"
				" fd=" << m_endpoint->GetHandle() << " err=" << errno);
			return RT_ERROR_NETWORK_SOCKET_ERROR;
		}
		else
			return RT_OK;
									   }

	case RT_OPT_TRANSPORT_RCV_BUF_LEN: {
		int nLen = sizeof(DWORD);
		if (m_endpoint->GetSocket().GetOption(SOL_SOCKET, SO_RCVBUF, aArg, &nLen) == -1) {
			RT_ERROR_TRACE_THIS("CRtTransportUdp::GetOption, GetOption(SO_RCVBUF) failed!"
				" fd=" << m_endpoint->GetHandle() << " err=" << errno);
			return RT_ERROR_NETWORK_SOCKET_ERROR;
		}
		else
			return RT_OK;
									   }

	case RT_OPT_TRANSPORT_SOCK_ALIVE: {
		if (m_endpoint->GetHandle() == RT_INVALID_HANDLE) {
			*static_cast<BOOL*>(aArg) = FALSE;
			return RT_ERROR_NOT_INITIALIZED;
		}
		else {
			*static_cast<BOOL*>(aArg) = TRUE;
			return RT_OK;
		}
									  }

	default:
		return CRtTransportBase::GetOption(aCommand, aArg);
	}
}

RtResult CRtTransportUdp::SetOption(DWORD aCommand, LPVOID aArg)
{
	if (m_endpoint==NULL)
		return RT_ERROR_NETWORK_SOCKET_CLOSE;

	RT_ASSERTE_RETURN(aArg, RT_ERROR_INVALID_ARG);

	switch (aCommand) {
	case RT_OPT_TRANSPORT_SND_BUF_LEN:
		if (m_endpoint->GetSocket().SetOption(SOL_SOCKET, SO_SNDBUF, aArg, sizeof(DWORD)) == -1) {
			RT_ERROR_TRACE_THIS("CRtTransportUdp::SetOption, SetOption(SO_SNDBUF) failed! err=" << errno);
			return RT_ERROR_NETWORK_SOCKET_ERROR;
		}
		else
			return RT_OK;

	case RT_OPT_TRANSPORT_RCV_BUF_LEN:
		if (m_endpoint->GetSocket().SetOption(SOL_SOCKET, SO_RCVBUF, aArg, sizeof(DWORD)) == -1) {
			RT_ERROR_TRACE_THIS("CRtTransportUdp::SetOption, SetOption(SO_RCVBUF) failed! err=" << errno);
			return RT_ERROR_NETWORK_SOCKET_ERROR;
		}
		else
			return RT_OK;

	case RT_OPT_TRANSPORT_TOS: 
		return SetTos2Socket(m_endpoint->GetSocket(), aArg);

	default:
		RT_WARNING_TRACE_THIS("CRtTransportUdp::SetOption,"
			" unknow aCommand=" << aCommand << 
			" aArg=" << aArg);
		return RT_ERROR_INVALID_ARG;
	}
}
